﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Acms.Exceptions
{
    public class Log
    {
        public Log()
        {
            Created = DateTime.Now;
        }

        public Guid Id { get; internal set; }
        public DateTime Created { get; internal set; }

        public int? SiteId { get; set; }
        public int? PostId { get; set; }
        public string SiteRoot { get; set; }
        public string AcmsUrl { get; set; }
        public int? UserID { get; set; }

        public string ServerName { get; internal set; }
        public string ApplicationRoot { get; internal set; }
        public string ApplicationPath { get; internal set; }
        public string Url { get; internal set; }
        public string Querystring { get; internal set; }
        public string Form { get; internal set; }
        public string Referer { get; internal set; }
        public string UserName { get; internal set; }

        public string ExceptionType { get; internal set; }
        public string ExceptionMessage { get; internal set; }
        public string InnerExceptionType { get; internal set; }
        public string InnerExceptionMessage { get; internal set; }
        public string Source { get; internal set; }
        public string StackTrace { get; internal set; }

        public void SetHttpContext(System.Web.HttpContext context)
        {
            this.ServerName = context.Request.ServerVariables["Server_Name"];
            this.ApplicationRoot = context.Request.ApplicationPath;
            this.ApplicationPath = context.Request.PhysicalApplicationPath;
            if (this.ApplicationRoot.EndsWith("/Acms", StringComparison.CurrentCultureIgnoreCase))
            {
                this.ApplicationPath = context.Server.MapPath("~/../");
            }
            this.Url = context.Request.AppRelativeCurrentExecutionFilePath;
            if (!string.IsNullOrEmpty(context.Request.Url.Query) && context.Request.Url.Query.Length > 1)
                this.Querystring = context.Request.Url.Query.Substring(1);
            this.Form = context.Request.Form.ToString();
            this.Referer = context.Request.ServerVariables["Http_Referer"];
            if (context.Request.IsAuthenticated)
                this.UserName = context.User.Identity.Name;
        }

        public void SetException(Exception exception)
        {
            this.ExceptionType = exception.GetType().ToString();
            this.ExceptionMessage = exception.Message;
            if (exception.InnerException != null)
            {
                this.InnerExceptionType = exception.InnerException.GetType().ToString();
                this.InnerExceptionMessage = exception.InnerException.Message;
            }
            this.Source = exception.Source;
            this.StackTrace = exception.StackTrace;
        }
    }

    public class LogFactory
    {
        private DB.ExceptionDBDataContext context = new Acms.Exceptions.DB.ExceptionDBDataContext();
        public void Save(Log log)
        {
            if (Properties.Settings.Default.LogExceptions)
            {
                DB.AcmsError dbRecord = null;
                if (log.Id != Guid.Empty)
                {
                    dbRecord = (from l in context.AcmsErrors where l.Id == log.Id select l).Single();
                }
                else
                {
                    dbRecord = new Acms.Exceptions.DB.AcmsError();
                    dbRecord.Id = log.Id = Guid.NewGuid();
                    context.AcmsErrors.InsertOnSubmit(dbRecord);
                }

                dbRecord.Created = log.Created.ToUniversalTime();
                dbRecord.ApplicationRoot = log.ApplicationRoot;
                dbRecord.ApplicationPath = log.ApplicationPath;
                dbRecord.ExceptionMessage = log.ExceptionMessage;
                dbRecord.ExceptionType = log.ExceptionType;
                dbRecord.Form = log.Form;
                dbRecord.InnerExceptionMessage = log.InnerExceptionMessage;
                dbRecord.InnerExceptionType = log.InnerExceptionType;
                dbRecord.PostId = log.PostId;
                dbRecord.Referer = log.Referer;
                dbRecord.ServerName = log.ServerName;
                dbRecord.SiteId = log.SiteId;
                dbRecord.SiteRoot = log.SiteRoot;
                dbRecord.Source = log.Source;
                dbRecord.StackTrace = log.StackTrace;
                dbRecord.AcmsUrl = log.AcmsUrl;
                dbRecord.Url = log.Url;
                dbRecord.Querystring = log.Querystring;
                dbRecord.UserID = log.UserID;
                dbRecord.UserName = log.UserName;

                context.SubmitChanges();
            }
        }

        private IQueryable<DB.AcmsError> GetBaseBySiteIdAndRoot(int siteId, string applicationPath)
        {
            return context.AcmsErrors.Where(x => x.SiteId == siteId && x.ApplicationPath == applicationPath).OrderByDescending(x => x.Created);
        }

        private List<Log> Convert(IQueryable<DB.AcmsError> db)
        {
            return db.Select(x => Convert(x)).ToList();
        }

        private Log Convert(DB.AcmsError db)
        {
            if (db != null)
            {
                return new Log()
                {
                    AcmsUrl = db.AcmsUrl,
                    ApplicationPath = db.ApplicationPath,
                    ApplicationRoot = db.ApplicationRoot,
                    Created = db.Created.ToLocalTime(),
                    ExceptionMessage = db.ExceptionMessage,
                    ExceptionType = db.ExceptionType,
                    Form = db.Form,
                    Id = db.Id,
                    InnerExceptionMessage = db.InnerExceptionMessage,
                    InnerExceptionType = db.InnerExceptionType,
                    PostId = db.PostId,
                    Referer = db.Referer,
                    ServerName = db.ServerName,
                    SiteId = db.SiteId,
                    SiteRoot = db.SiteRoot,
                    Source = db.Source,
                    StackTrace = db.StackTrace,
                    Url = db.Url,
                    UserID = db.UserID,
                    UserName = db.UserName,
                    Querystring = db.Querystring
                };
            }
            else
            {
                return null;
            }
        }

        public List<Log> GetAllBySiteIdAndRoot(int siteId, string applicationPath)
        {
            var data = GetBaseBySiteIdAndRoot(siteId, applicationPath);
            return Convert(data);
        }

        public List<Log> Search(int siteId, string applicationPath, string exceptionType, string url, DateTime? from, DateTime? to)
        {
            var data = GetBaseBySiteIdAndRoot(siteId, applicationPath);
            if (!string.IsNullOrEmpty(exceptionType))
            {
                data = data.Where(x => x.ExceptionType == exceptionType);
            }
            if (!string.IsNullOrEmpty(url))
            {
                data = data.Where(x => x.Url == url);
            }
            if (from != null)
            {
                data = data.Where(x => x.Created >= from.Value.ToUniversalTime());
            }
            if (to != null)
            {
                data = data.Where(x => x.Created < to.Value.ToUniversalTime());
            }
            return Convert(data);
        }

        public string[] GetExceptionTypesBySiteIdAndRoot(int siteId, string applicationPath)
        {
            var data = GetBaseBySiteIdAndRoot(siteId, applicationPath).OrderBy(x => x.ExceptionType).Select(x => x.ExceptionType).Distinct();
            return data.ToArray();
        }

        public string[] GetUrlsBySiteIdAndRoot(int siteId, string applicationPath)
        {
            var data = GetBaseBySiteIdAndRoot(siteId, applicationPath).OrderBy(x => x.ExceptionType).Select(x => x.Url).Distinct();
            return data.ToArray();
        }
    }
}
